package nl.zuiderfestival.raydance.view.canvas;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;

import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.border.EtchedBorder;

import nl.zuiderfestival.raydance.model.RayDance;
import nl.zuiderfestival.raydance.model.RayEvent;
import nl.zuiderfestival.raydance.model.RayEventType;
import nl.zuiderfestival.raydance.model.zones.Zone;

public class ZoneDisplay extends JComponent implements Observer {

	public static final String SHAPE_SELECTION = "shapeSelection";

	private static final long serialVersionUID = 1L;

	private Set<ZoneShape> zones;

	private Dimension dimension;
	private ZoneShape selectedItem;
	
	public void addActionListener(ActionListener l) {
		listenerList.add(ActionListener.class, l);
	}
	
	public ZoneDisplay(int size) {
		super();
		
		setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
		zones = new HashSet<ZoneShape>();

		dimension = new Dimension(size, size);
		setPreferredSize(dimension);

		ZoneDragHandler l = new ZoneDragHandler(this); 
		addMouseListener(l);
		addMouseMotionListener(l);
	}

	@SuppressWarnings("unchecked")
	public void update(Observable o, Object arg) {
		if (o instanceof RayDance && arg instanceof RayEvent) {
			RayEvent event = (RayEvent) arg;

			if (event.getType() == RayEventType.INIT) {
				Set<Zone> initZones = (Set<Zone>) event.getData();
				for (Zone z : initZones) {
					zones.add(createZoneShape(z));
					z.addObserver(this);
				}

			} else if (event.getType() == RayEventType.ZONEADDED) {
				Zone zone = (Zone)event.getData();
				zones.add(createZoneShape(zone));
				zone.addObserver(this);
			} else if (event.getType() == RayEventType.ZONEREMOVED) {
				Zone zone = (Zone)event.getData();
				zones.remove(getZoneShape(zone));
				zone.deleteObserver(this);
			} else if (event.getType() == RayEventType.SELECTIONCHANGED) {
				Zone zone = (Zone)event.getData();
				if (zone == null) {
					deselectZone();
				} else {
					selectZone(zone);
				}
			}
			
			repaint();
		} else if (o instanceof Zone) {
			repaint();
		}
	}

	private void selectZone(Zone zone) {
		if (selectedItem != null) {
			selectedItem.setSelected(false);
		}
		selectedItem = getZoneShape(zone);
		selectedItem.setSelected(true);
		
	}

	private void deselectZone() {
		if (selectedItem != null) {
			selectedItem.setSelected(false);
		}
		selectedItem = null;
	}

	@Override
	protected void paintComponent(Graphics g) {
		super.paintComponent(g);
		Graphics2D g2d = (Graphics2D) g;
		g2d.setColor(Color.black);
		g2d.fill(new Rectangle(dimension));
		
		for (ZoneShape z : zones) {
			z.draw(g2d);
		}
	}

	public boolean hasShapeAt(Point point) {
		return (getShapeAt(point) != null);
	}

	public ZoneShape getShapeAt(Point point) {
		ZoneShape result = null;
		
		if (selectedItem!=null && selectedItem.getRectangle().contains(point)) {
			result = selectedItem;
		}
		
		Iterator<ZoneShape> iterator = zones.iterator();
		while (iterator.hasNext() && result == null) {
			ZoneShape zs = iterator.next();
			if (zs.getRectangle().contains(point)) {
				result = zs;
			}
		}
		return result;
	}

	public ZoneShape getSelectedItem() {
		return selectedItem;
	}

	public void setSelectedItem(ZoneShape selectedItem) {
		if (this.selectedItem != null) {
			this.selectedItem.setSelected(false);
		}
		this.selectedItem = selectedItem;
		this.selectedItem.setSelected(true);
		fireActionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, SHAPE_SELECTION));
	}

	private ZoneShape getZoneShape(Zone zone) {
		ZoneShape result = null;
		Iterator<ZoneShape> iterator = zones.iterator();
		while (iterator.hasNext() && result == null) {
			ZoneShape zs = iterator.next();
			if (zs.equals(zone)) {
				result = zs;
			}
		}
		return result;
	}

	private ZoneShape createZoneShape(Zone zone) {
		Rectangle zoneShape = new Rectangle();
		return new ZoneShape(zone, zoneShape);
	}

	private String getActionCommand() {
		return SHAPE_SELECTION;
	}
	
    private void fireActionPerformed(ActionEvent event) {
    	// Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        ActionEvent e = null;
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length-2; i>=0; i-=2) {
            if (listeners[i]==ActionListener.class) {
                // Lazily create the event:
                if (e == null) {
                      String actionCommand = event.getActionCommand();
                      if(actionCommand == null) {
                         actionCommand = getActionCommand();
                      }
                      e = new ActionEvent(ZoneDisplay.this,
                                          ActionEvent.ACTION_PERFORMED,
                                          actionCommand,
                                          event.getWhen(),
                                          event.getModifiers());
                }
                ((ActionListener)listeners[i+1]).actionPerformed(e);
            }
        }
    }

}
